package pl.cba.solgar.game.rogal;

import org.newdawn.slick.Color;

import pl.cba.solgar.game.rogal.commands.Command;
import pl.cba.solgar.game.rogal.console.Console;
import pl.cba.solgar.game.rogal.utils.BSTNode;
import pl.cba.solgar.game.rogal.utils.BinarySearchTree;

public class CommandRecognizer {
	final private BinarySearchTree root;
	private BinarySearchTree current;
	private Command defaultCommand;
	private Command currentDefaultCommand;

	private String commandChars;

	public CommandRecognizer() {
		root = new BinarySearchTree();
		defaultCommand = null;
		reset();
	}

	private void reset() {
		current = root;
		commandChars = "";
		currentDefaultCommand = defaultCommand;
	}

	public String getChars() {
		return commandChars;
	}

	public void recognize(char c) {
		commandChars += c;
		BSTNode<?> node = current.find(c);
		if (node == null) {
			if (currentDefaultCommand == null) {
				MessageConsole.getInstance().postMessage("Unknown command \""+commandChars+"\".");
				reset();
			}
			else {
				if (currentDefaultCommand.execute())
					reset();
				commandChars = "";
			}
		}
		else if (node.getValue() instanceof Command) {
			if (((Command) node.getValue()).execute())
				reset();
		}
		else if (node.getValue() instanceof BinarySearchTree) {
			current = (BinarySearchTree) node.getValue();
		}
	}

	public void addCommand(Command cmd) {
		addCommand(cmd, current);
	}

	public void addCommand(Command cmd, BinarySearchTree ctree) {
		String chars = cmd.getKeys();
		for (int i = 0; i < chars.length(); i++) {
			BSTNode<?> n = ctree.find(chars.charAt(i));

			// if the iteration is last one...
			if (i == chars.length() - 1) {
				// there should be no node in tree witch such chars
				if (n != null) {
					throw new RuntimeException("CommandRecognizer.addCommand: command with chars ["+chars+"] adding error. Such command exist!");
				}
				// now we can add our command
				ctree.addNode(new BSTNode<Command>(chars.charAt(i), cmd));
			}
			// handling of longer commands
			else {
				// if no such node lets create one
				if (n == null) {
					BinarySearchTree bst = new BinarySearchTree();
					ctree.addNode(new BSTNode<BinarySearchTree>(chars.charAt(i), bst));
					ctree = bst;
				}
				else if (n.getValue() instanceof BinarySearchTree) {
					ctree = (BinarySearchTree) n.getValue();
				}
				else {
					throw new RuntimeException("CommandRecognizer.addCommand: command with chars ["+chars+"]. Command ["+chars.charAt(i)+"] already exist!");
				}
			}
		}
		// reset current tree to root tree
		reset();
	}

	public BinarySearchTree getCurrentTree() {
		return current;
	}

	public void setCurrentTree(BinarySearchTree current) {
		this.current = current;
	}

	public BinarySearchTree getRoot() {
		return root;
	}

	public Command getDefaultCommand() {
		return defaultCommand;
	}

	public Command getCurrentDefaultCommand() {
		return currentDefaultCommand;
	}

	public void setCurrentDefaultCommand(Command currentDefaultCommand) {
		this.currentDefaultCommand = currentDefaultCommand;
	}

	int printLine;
	public void printCommands(Console c) {
		printCommands(c, root, "");
	}

	public void printCommands(Console c, BinarySearchTree tree, String prefix) {
		printLine = 0;
		BSTNode<?> n = tree.getRoot();
		c.setColor(Color.white);
		printNode(n, prefix, c);
		c.print("count: " + printLine, 0, printLine);
	}

	private void printNode(BSTNode<?> node, String prefix, Console c) {
		if (node == null)
			return;

		printNode(node.getLeft(), prefix, c);
		if (node.getValue() instanceof BinarySearchTree) {
			BinarySearchTree bst = (BinarySearchTree) node.getValue();
			printNode(bst.getRoot(), prefix + (char) node.getKey(), c);
		} else {
			Command cmd = (Command) node.getValue();
			c.print(prefix + (char) node.getKey() + " -> " + cmd.getShortDescription(), 0, printLine++);
		}
		printNode(node.getRight(), prefix, c);
	}

}
